// 定义包路径
package com.lxx.jmb2c.controller;

// 导入相关类
import com.lxx.jmb2c.common.Result;
import com.lxx.jmb2c.dto.MemberBindAccountDTO;
import com.lxx.jmb2c.dto.MemberLoginDTO;
import com.lxx.jmb2c.dto.MemberRegisterDTO;
import com.lxx.jmb2c.dto.MemberUpdatePasswordDTO;
import com.lxx.jmb2c.entity.Member;
import com.lxx.jmb2c.service.MemberService;
import com.lxx.jmb2c.vo.MemberInfoVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.Random;

/**
 * 会员控制器
 * 
 * 这个控制器处理所有与会员用户相关的HTTP请求，包括：
 * 1. 会员登录/注册
 * 2. 获取会员信息
 * 3. 修改密码
 * 4. 账号绑定
 * 
 * 初学者需要了解：
 * - @RestController 表示这是一个控制器，会自动将返回对象转为JSON
 * - @RequestMapping 定义了基础路径为"/member"，所以所有接口URL都以/member开头
 * - @Resource 是依赖注入注解，相当于自动创建MemberService实例
 * 
 * Knife4j文档说明：
 * 这个控制器下的所有接口都会在Knife4j文档中显示，可以通过
 * http://localhost:8080/doc.html 访问
 * 使用@Tag定义分组，@Operation定义接口说明，@ApiResponses定义响应状态
 * 
 * @author lxx
 * @since 2025-07-02
 */
// 启用日志功能
@Slf4j
// 标记为REST控制器，返回JSON格式数据
@RestController
@Tag(name="会员管理")
// 设置请求路径前缀
@RequestMapping("/member")
public class MemberController {
    // 注入会员服务
    @Resource
    private MemberService memberService;

    // 当redis中的键值对，键和值的数据类型都是String时可以改用StringRedisTemplate
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 用户登录接口
     * 
     * 处理会员用户的账号密码登录请求，成功返回JWT token用于后续请求认证
     * 
     * @param memberLoginDTO 登录信息数据传输对象，包含账号和密码
     * @return 登录结果，包含JWT token
     * 
     * TODO 需要实现的功能：
     * 1. 添加账号锁定机制，防止暴力破解
     * 2. 实现登录日志记录
     * 
     * 初学者提示：
     * - @Operation 定义API在Knife4j文档中的显示信息
     * - @Parameter 描述请求参数，required=true表示必填
     * - @ApiResponses 定义可能的响应状态码和说明
     * - @RequestBody 表示从请求体获取JSON数据
     * - log.info() 用于记录日志，开发阶段可用于调试
     * 
     * 常见问题：
     * 1. 401错误：检查账号密码是否正确
     * 2. 500错误：查看服务端日志排查问题
     */
    @Operation(
        summary = "用户登录",
        description = "使用账号密码进行登录认证",
        parameters = {
            @Parameter(name = "memberLoginDTO", description = "登录凭证", required = true,
                      content = @Content(schema = @Schema(implementation = MemberLoginDTO.class)))
        }
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "登录成功",
            content = @Content(schema = @Schema(implementation = Result.class))),
        @ApiResponse(responseCode = "401", description = "用户名或密码错误"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @PostMapping("login")
    public Result<String> login(
        @RequestBody 
        @Parameter(description = "登录请求参数", required = true,
                  content = @Content(schema = @Schema(implementation = MemberLoginDTO.class)))
        MemberLoginDTO memberLoginDTO) {
        // 调用服务层处理登录逻辑，获取token
        String token = memberService.login(memberLoginDTO);
        // 返回成功结果，包含token
        return Result.success(token);
    }

    /**
     * 获取用户信息接口
     * 
     * 根据请求头中的JWT token获取当前登录会员的详细信息
     * 通常用于登录后获取用户基本信息展示
     * 
     * @return 返回用户信息，包含昵称、头像等基本信息
     * 
     * TODO 需要实现的功能：
     * 1. 添加权限验证，确保只能获取自己的信息
     * 2. 实现信息缓存，提高性能
     * 
     * 初学者提示：
     * - 这个接口需要在登录后调用，请求头需携带Authorization: Bearer {token}
     * - @GetMapping 表示这是一个GET请求
     * - 返回的信息可能包括：昵称、头像、手机号等(根据实际业务)
     * 
     * 常见问题：
     * 1. 401错误：检查token是否有效/过期
     * 2. 500错误：查看服务端日志排查问题
     */
    @Operation(
        summary = "获取用户信息",
        description = "获取当前登录用户的详细信息"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功",
            content = @Content(schema = @Schema(implementation = Result.class))),
        @ApiResponse(responseCode = "401", description = "未授权"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @GetMapping("info")
    public Result<MemberInfoVO> info() {
        // 调用服务层获取用户信息
        MemberInfoVO member = memberService.info();
        // 返回成功结果，包含用户信息
        return Result.success(member);
    }

    /**
     * 更新用户密码接口
     * 
     * 修改当前登录会员的密码，需要验证原密码
     * 使用PUT请求，因为这是一个幂等操作（多次执行结果一致）
     * 
     * @param memberUpdatePasswordDTO 密码更新信息，包含原密码和新密码
     * @return 操作结果
     * 
     * TODO 需要实现的功能：
     * 1. 添加密码强度验证
     * 2. 实现密码修改日志记录
     * 
     * 初学者提示：
     * - @PutMapping 表示幂等操作，多次调用结果相同
     * - 幂等性：无论调用1次还是多次，最终密码都会被更新为相同值
     * - 密码应该加密存储，不要明文保存
     * - 前端应该对新密码做强度校验
     * 
     * 安全注意事项：
     * 1. 必须验证原密码
     * 2. 新密码不能与原密码相同
     * 3. 建议定期强制修改密码
     */
    @Operation(
        summary = "更新密码",
        description = "修改当前登录用户的密码",
        parameters = {
            @Parameter(name = "memberUpdatePasswordDTO", description = "密码更新信息", required = true,
                     content = @Content(schema = @Schema(implementation = MemberUpdatePasswordDTO.class)))
        }
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "密码更新成功"),
        @ApiResponse(responseCode = "400", description = "参数验证失败"),
        @ApiResponse(responseCode = "401", description = "未授权"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @PutMapping("updatePassword")
    public Result<Object> updatePassword(
        @RequestBody 
        @Parameter(description = "密码更新请求参数", required = true,
                 content = @Content(schema = @Schema(implementation = MemberUpdatePasswordDTO.class)))
        MemberUpdatePasswordDTO memberUpdatePasswordDTO) {
        // 调用服务层更新密码
        memberService.updatePassword(memberUpdatePasswordDTO);
        // 返回成功结果
        return Result.success();
    }

    /**
     * 用户注册接口
     * 
     * 处理新会员的注册请求，需要验证手机号和邮箱
     * 完整流程：
     * 1. 前端调用sendCode发送验证码
     * 2. 用户输入验证码后提交注册
     * 3. 后端验证验证码是否正确
     * 
     * @param memberRegisterDTO 注册信息，包含手机号、邮箱、密码和验证码
     * @return 注册成功的会员信息
     * 
     * TODO 需要实现的功能：
     * 1. 添加防刷机制，防止恶意注册
     * 2. 实现注册欢迎邮件发送
     * 
     * 初学者提示：
     * - @PostMapping 表示非幂等操作，每次注册都会创建新用户
     * - 验证码应该有时效性(如5分钟过期)
     * - 密码需要加密存储
     * - 409冲突响应表示手机号或邮箱已被注册
     * 
     * 业务规则：
     * 1. 手机号和邮箱必须验证
     * 2. 验证码为6位数字
     * 3. 密码需满足复杂度要求
     */
    @Operation(
        summary = "用户注册",
        description = "新用户注册账号，需要验证手机号和邮箱"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "注册成功",
            content = @Content(schema = @Schema(implementation = Result.class))),
        @ApiResponse(responseCode = "400", description = "参数验证失败"),
        @ApiResponse(responseCode = "409", description = "手机号或邮箱已存在"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @PostMapping("register")
    public Result<Member> register(
        @RequestBody 
        @Parameter(description = "注册信息", required = true,
                 content = @Content(schema = @Schema(implementation = MemberRegisterDTO.class)))
        MemberRegisterDTO memberRegisterDTO){
        // 调用服务层注册
        log.info("memberRegisterDTO:{}",memberRegisterDTO);
        Member member = memberService.register(memberRegisterDTO);
        log.info("Controller:member:{}",member);
        // 返回成功结果
        return Result.success(member);
    }


    /**
     * 发送验证码接口
     * 
     * 向用户手机或邮箱发送6位数字验证码，用于注册或敏感操作验证
     * 验证码会存储在Redis中，默认5分钟过期
     * 
     * @param memberRegisterDTO 接收验证码的手机或邮箱信息
     * @return 发送结果
     * 
     * TODO 需要实现的功能：
     * 1. 添加发送频率限制
     * 2. 实现不同业务场景的验证码(注册、找回密码等)
     * 
     * 初学者提示：
     * - Redis用于临时存储验证码，保证分布式环境下可用
     * - 验证码应该设置过期时间，防止长期有效
     * - 生产环境需要接入短信/邮件服务
     * 
     * 安全注意事项：
     * 1. 不要返回验证码给前端
     * 2. 需要防刷机制
     * 3. 验证码不要使用简单模式(如000000)
     */
    @Operation(
        summary = "发送验证码",
        description = "向用户手机或邮箱发送6位数字验证码，有效期5分钟"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "验证码发送成功"),
        @ApiResponse(responseCode = "400", description = "参数验证失败"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @PostMapping("sendCode")
    public Result<Object> sendCode(
        @RequestBody 
        @Parameter(description = "接收验证码的手机或邮箱信息", required = true,
                 content = @Content(schema = @Schema(implementation = MemberRegisterDTO.class)))
        MemberRegisterDTO memberRegisterDTO){
        //发送验证码
        Random random = new Random();
        String code = random.nextInt(888888)+100000+"";
        //将验证码存入redis
        redisTemplate.opsForValue().set("code",code);
        log.info("验证码："+code);
        return Result.success();
    }

    /**
     * 绑定账号接口
     * 
     * 将手机号、邮箱或第三方开放ID绑定到当前会员账号
     * 使用PUT请求，因为这是一个幂等操作
     * 
     * @param memberBindAccountDTO 绑定账号信息
     * @return 操作结果
     * 
     * TODO 需要实现的功能：
     * 1. 添加绑定验证流程
     * 2. 实现绑定通知功能
     * 
     * 初学者提示：
     * - @PutMapping 表示幂等操作
     * - 幂等性：无论调用1次还是多次，最终绑定结果相同
     * - 409冲突响应表示账号已被其他用户绑定
     * 
     * 业务规则：
     * 1. 一个手机号/邮箱只能绑定一个账号
     * 2. 绑定需要验证账号所有权
     * 
     * 安全注意事项：
     * 1. 必须验证当前用户身份
     * 2. 敏感操作需要二次验证
     */
    @Operation(
        summary = "绑定账号",
        description = "绑定手机号、邮箱或第三方开放ID到当前账号"
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "绑定成功"),
        @ApiResponse(responseCode = "400", description = "参数验证失败"),
        @ApiResponse(responseCode = "401", description = "未授权"),
        @ApiResponse(responseCode = "409", description = "账号已被绑定"),
        @ApiResponse(responseCode = "500", description = "系统内部错误")
    })
    @PutMapping("bindPhoneOrEmailOrOpenId")
    public Result<Object> bindPhoneOrEmailOrOpenId(
        @RequestBody 
        @Parameter(description = "绑定账号信息", required = true,
                 content = @Content(schema = @Schema(implementation = MemberBindAccountDTO.class)))
        MemberBindAccountDTO memberBindAccountDTO){
        log.info("memberBindAccountDTO:{}",memberBindAccountDTO);
        this.memberService.bindPhoneOrEmailOrOpenId(memberBindAccountDTO);
        return Result.success();
    }
}
